This post will show you how to authenticate for the client credentials flow in PowerShell with MSAL.Net using the .pfx file for the certificate authentication instead of loading the certificate from the certificate store. This post is in part based on this: Using PowerShell to Configure a signing certificate for a SAML-based SSO enterprise application Also, special thanks to my team members Bac Hoang and Will Fiddes for assisting with this sample. I am assuming that you already know the proper steps to create a self signed certificate and also you’re familiar with the client credentials flow. Please note that the public key cer file of the same certificate (of which the private key is used in this post) should be configured on the same app registration. Please refer to this blog post for the requirements.

Reference

Methods in this powershell script

  • Load-MSAL – ensures that the proper version of MSAL is installed for the version of PowerShell you’re using.
  • GetX509Certificate_FromPfx – this will return the certificate from the .Pfx file. You will need the full path to the .pfx file and the password for the pfx.
  • Get-GraphAccessTokenFromMSAL – This will Authenticate and retrieve an access token for the graph endpoint.
$ClientID           = "{client_id}"
$loginURL           = "https://login.microsoftonline.com"
$tenantdomain       = "{tenant_id}" 
$CertPassWord       = "{password_for_cert}"
$certPath           = "C:\temp\Certs\testCert_01.pfx"

[string[]] $Scopes  = "https://graph.microsoft.com/.default"

Function Load-MSAL {
    if ($PSVersionTable.PSVersion.Major -gt 5)
    { 
        $core = $true
        $foldername =  "netcoreapp2.1"
    }
    else
    { 
        $core = $false
        $foldername = "net45"
    }

    # Download MSAL.Net module to a local folder if it does not exist there
    if ( ! (Get-ChildItem $HOME/MSAL/lib/Microsoft.Identity.Client.* -erroraction ignore) ) {
        install-package -Source nuget.org -ProviderName nuget -SkipDependencies Microsoft.Identity.Client -Destination $HOME/MSAL/lib -force -forcebootstrap | out-null
    }
  
    # Load the MSAL assembly -- needed once per PowerShell session
    [System.Reflection.Assembly]::LoadFrom((Get-ChildItem $HOME/MSAL/lib/Microsoft.Identity.Client.*/lib/$foldername/Microsoft.Identity.Client.dll).fullname) | out-null
  }
 
Function Get-GraphAccessTokenFromMSAL {

    Load-MSAL

    $global:app = $null

    $x509cert = [System.Security.Cryptography.X509Certificates.X509Certificate2] (GetX509Certificate_FromPfx -CertificatePath $certPath -CertificatePassword $CertPassWord)
    write-host "Cert = {$x509cert}"

    $ClientApplicationBuilder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ClientID)
        [void]$ClientApplicationBuilder.WithAuthority($("$loginURL/$tenantdomain"))
        [void]$ClientApplicationBuilder.WithCertificate($x509cert)
    $global:app = $ClientApplicationBuilder.Build()

    [Microsoft.Identity.Client.AuthenticationResult] $authResult  = $null
    $AquireTokenParameters = $global:app.AcquireTokenForClient($Scopes)
    try {
        $authResult = $AquireTokenParameters.ExecuteAsync().GetAwaiter().GetResult()
    }
    catch {
        $ErrorMessage = $_.Exception.Message
        Write-Host $ErrorMessage
    }
    
    return $authResult
}

function GetX509Certificate_FromPfx($CertificatePath, $CertificatePassword){
    #write-host "Path: '$CertificatePath'"
   
    if(![System.IO.Path]::IsPathRooted($CertificatePath))
    {
        $LocalPath = Get-Location
        $CertificatePath = "$LocalPath\$CertificatePath"
    }

    #Write-Host "Looking for '$CertificatePath'"

    $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificatePath, $CertificatePassword)
    
    Return $certificate
}

$myvar = Get-GraphAccessTokenFromMSAL
Write-Host "Access Token: " $myvar.AccessToken

Summary

Loading the certificate from a .pfx file in PowerShell can make it easier for an admin to manage certificates without having to install the certificate in the certificate store. However, this should not be done on a client machine as the user could potentially discover the file and also the password for it, as well as the method to authenticate. The client credentials flow is only intended to be ran in a back-end service to service type of scenario where only admins have access to the machine.

6 Thoughts to “MSAL.Net in PowerShell – Use .pfx file for Client Credentials Flow”

  1. andrew stevens

    Thanks for this, really helpful, keep up the good work!

    1. Bac Hoang [MSFT]

      Glad to hear it’s helpful.

  2. Manu

    Quick question tough, would that work on a Linux devops agent ?
    I have an error with the Install-Package

    1. Ray Held [MSFT]

      It should work however, I don’t work with Linux so I cannot answer that question for you.

  3. vivek mishra

    Thanks for the post. Quick question, my PFX file doesn’t have password. I downloaded it from keyvault (both cer and pfx) which doesn’t gives the password for PFX.
    Can I use this solution without a password for PFX file.

    1. Ray Held [MSFT]

      You should be able to set the password to null or empty string. You can give it a try and see if it works. I always use a password so I really can’t answer that question with 100% certainty.

Leave a Comment